#include "planner.h"

    template<> Planner* Singleton<Planner>::msSingleton = 0;
    Planner* Planner::getSingletonPtr(void)
    {
        return msSingleton;
    }
    //-----------------------------------------------------------------------
    Planner& Planner::getSingleton(void)
    {
        assert( msSingleton );  return ( *msSingleton );
    }
    //-----------------------------------------------------------------------
    Planner::Planner()
    {
        readyProcessList = new PriorityList(100, 0);
        runningProcessId = 0;
        processCount = 0;
    }
    //-----------------------------------------------------------------------
    Planner::~Planner()
    {
        delete readyProcessList;
    }

int Planner::createProcess(const int rParentId, State rState, int rPriority, Process* rProcess)
{
    for(processListIterator it = processList.begin(); it != processList.end(); ++it)
    {
        if((*it)->id == rParentId){
            (*it)->childProcessList.push_back(processCount);
            break;
        }
    }

    rProcess->id = processCount;
    rProcess->parentId = rParentId;
    rProcess->priority = rPriority;
    rProcess->state = rState;
    processList.push_back(rProcess);

    if(rState == READY)
    {
        readyProcessList->add(rPriority, processCount);
    }

    qDebug() << processCount;
    return processCount++;
}

void Planner::deleteProcess(const int rId)
{
    for (processListIterator it = processList.begin(); it != processList.end(); ++it)
    {
        if((*it)->id == rId){
            intListBase childProcesses = (*it)->childProcessList;
            for(intListIterator ilt = childProcesses.begin(); ilt != childProcesses.end(); ++ilt)
            {
                deleteProcess((*ilt));
            }
            qDebug() << "Delete process(" << (*it)->priority << "):[" << (*it)->state << "]" << rId;
            if((*it)->state == READY) {
                readyProcessList->remove(rId);
            }
            delete (*it);
            processList.erase(it);
            break;
        }
    }
}

void Planner::activateProcess(const int rId)
{
    Process* process = findProcess(rId);
    if(process != 0) {
        switch(process->state) {
            case READY_STOPPED: {
                process->state = READY;
                readyProcessList->add(process->priority, process->id);
            }break;
            case BLOCKED_STOPPED: {
                process->state = BLOCKED;
            }break;
            default: {
                qDebug() << "Can't activate currently active process!";
            }
        }
    }else {
        qDebug() << "Can't find process " << rId << " @ Planner::activateProcess";
    }
}

void Planner::stopProcess(const int rId)
{
    Process* process = findProcess(rId);
    if(process != 0) {
        if(process->state == READY) {
            readyProcessList->remove(rId);
        }else if(process->state == RUN) {
            runningProcessId = -1;
        }
        process->state = READY_STOPPED;
    }else {
        qDebug() << "Can't find process " << rId << " @ Planner::stopProcess";
    }
}

void Planner::changeProcessPriority(const int rId, int rPriority)
{
    Process* process = findProcess(rId);
    if(process != 0) {
        process->priority = rPriority;
        if(process->state == READY) {
            readyProcessList->remove(rId);
            readyProcessList->add(rPriority, rId);
        }
    }else {
        qDebug() << "Can't find process " << rId << " @ Planner::changeProcessPriority";
    }
}

void Planner::blockProcess(const int rId)
{
    Process* process = findProcess(rId);
    if(process != 0) {
        if(process->state == READY) {
            readyProcessList->remove(rId);
        }else if(process->state == RUN) {
            runningProcessId = -1;
        }
        process->state = BLOCKED;
    }else {
        qDebug() << "Can't find process " << rId << " @ Planner::blockProcess";
    }
}
void Planner::giveResourceToProcess(const int rId,Resource* rResource)
{
    Process* process = findProcess(rId);
    if(process != 0) {
        process->usingResourceList.push_back(rResource);
        if(process->state != READY) {
            readyProcessList->add(process->priority, rId);
            process->state = READY;
        }else {
            qDebug() << "PROCESS IS READY WTF HURR DURR @ Planner::giveResourceToProcess";
        }
    }else {
        qDebug() << "Can't find process " << rId << " @ Planner::giveResourceToProcess";
    }
}
void Planner::plan() {
    if(runningProcessId >= 0) {
        Process* runningProcess = findProcess(runningProcessId);
        if(runningProcess != 0) {
            int topProcessId = readyProcessList->top();
            if(topProcessId >= 0) {
                Process* topProcess = findProcess(topProcessId);
                if(topProcess != 0) {
                    if(runningProcess->priority < topProcess->priority) {
                        runningProcess->state = READY;
                        topProcess->state = RUN;
                        readyProcessList->remove(topProcess->id);
                        readyProcessList->add(runningProcess->priority, runningProcess->id);
                        runningProcessId = topProcess->id;
                    }else {
                        qDebug() << "process with higher priority than running process was not found @ Planner::plan";
                    }
                }else {
                    qDebug() << "gg @ Planner::plan";
                }
            }else {
                qDebug() << "no ready processes @ Planner::plan";
            }
        }else {
            qDebug() << "dafuq @ Planner::plan";
        }
    }else {
        int topProcessId = readyProcessList->top();
        if(topProcessId >= 0) {
            Process* topProcess = findProcess(topProcessId);
            if(topProcess != 0) {
                topProcess->state = RUN;
                readyProcessList->remove(topProcess->id);
                runningProcessId = topProcess->id;
            }else {
                qDebug() << "uber gg @ Planner::plan";
            }
        }else {
            qDebug() << "No processes at all @ Planner::plan! You should definately create at least one process...";
        }
    }
}
void Planner::Run() {
    qDebug() << "Planner::Run";
    if(runningProcessId < 0) {
        plan();
    }else {
        Process* runningProcess = findProcess(runningProcessId);
        if(runningProcess->state == RUN) {
            runningProcess->Run();
        }else {
            qDebug() << "Currently running process state isn't RUN! @ Planner::Run";
        }
    }
}

Process* Planner::findProcess(const int rId) {
    Process* result = 0;
    for (processListIterator it = processList.begin(); it != processList.end(); ++it)
    {
        if((*it)->id == rId){   
            result = *it;
            break;
        }
    }

    return result;
}
